package org.kessy.store.impl;

import java.util.ArrayList;
import java.util.Collection;

import javax.naming.Context;
import javax.naming.InitialContext;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.kessy.common.model.meta.MBase;
import org.kessy.common.param.MetaQueryParam;
import org.kessy.store.api.MetaStoreIFC;

/***
 * this is one implementation of MetaStoreIFC, it will use hibernate 
 * to provide O/R mapping
 * @author leo.liu
 *
 */
public class MetaStoreHibernateImpl implements MetaStoreIFC{
    private final static Logger logger = LoggerFactory.getLogger(MetaStoreHibernateImpl.class);

    public void persistMBase(MBase mBase) {
        long beginMillis = System.currentTimeMillis();
        try{
            innerPersistMBase(mBase);
        }
        catch(RuntimeException rex){
            logger.error(rex.getMessage(), rex);
            throw rex;
        }
        finally{
            if(logger.isDebugEnabled()){
                long endMillis = System.currentTimeMillis();
                logger.debug("time consume: " + (endMillis - beginMillis));
            }
        }
        
    }

    public void persistMBases(Collection<MBase> mBases) {
        long beginMillis = System.currentTimeMillis();
        try{
            innerPersistMBases(mBases);
        }
        catch(RuntimeException rex){
            logger.error(rex.getMessage(), rex);
            throw rex;
        }
        finally{
            if(logger.isDebugEnabled()){
                long endMillis = System.currentTimeMillis();
                logger.debug("time consume: " + (endMillis - beginMillis));
            }
        }
    }

    public MBase queryMBase(MetaQueryParam metaQueryParam) {
        long beginMillis = System.currentTimeMillis();
        try{
            return innerQueryMBase(metaQueryParam);
        }
        catch(RuntimeException rex){
            logger.error(rex.getMessage(), rex);
            throw rex;
        }
        finally{
            if(logger.isDebugEnabled()){
                long endMillis = System.currentTimeMillis();
                logger.debug("time consume: " + (endMillis - beginMillis));
            }
        }
    }

    public int queryMBaseCount(MetaQueryParam metaQueryParam) {
        long beginMillis = System.currentTimeMillis();
        try{
            return innerQueryMBaseCount(metaQueryParam);
        }
        catch(RuntimeException rex){
            logger.error(rex.getMessage(), rex);
            throw rex;
        }
        finally{
            if(logger.isDebugEnabled()){
                long endMillis = System.currentTimeMillis();
                logger.debug("time consume: " + (endMillis - beginMillis));
            }
        }
    }

    public Collection<MBase> queryMBases(MetaQueryParam metaQueryParam) {
        long beginMillis = System.currentTimeMillis();
        Session session = null;
        try{
            session = getSessionFactory().openSession();
            return innerQueryMBases(metaQueryParam, session);
        }
        catch(RuntimeException rex){
            logger.error(rex.getMessage(), rex);
            throw rex;
        }
        finally{
            if(session != null){
                session.close();
        	}
            if(logger.isDebugEnabled()){
                long endMillis = System.currentTimeMillis();
                logger.debug("time consume: " + (endMillis - beginMillis));
            }
        }
    }
    
    private void innerPersistMBase(MBase mBase){
        
    }
    
    private void innerPersistMBases(Collection<MBase> mBases){
        
    }
    
    private MBase innerQueryMBase(MetaQueryParam metaQueryParam){
        return null;
    }
    
    private int innerQueryMBaseCount(MetaQueryParam metaQueryParam){
        return 0;
    }

    private Collection<MBase> innerQueryMBases(MetaQueryParam metaQueryParam, Session session){
        Query query = session.createQuery(metaQueryParam.getHql());
        if (true == metaQueryParam.isPaged()) {
            query.setFirstResult(metaQueryParam.getFirstResult());
            query.setMaxResults(metaQueryParam.getMaxResults());
        }
		
        Collection<MBase> cResult = (Collection<MBase>)query.list();
        Collection<MBase> resultToReturn = new ArrayList<MBase>();
		
        if(cResult != null && cResult.size() > 0){
            for(MBase mBase:cResult){
                mBase = this.assembleMObject(mBase, metaQueryParam.getAssembleString(), metaQueryParam);
                resultToReturn.add(mBase);
            }
        }
		
        return resultToReturn;
    }
    
    private MBase assembleMObject(MBase mBase, String assembleString, MetaQueryParam metaQueryParam){
        // need to implement later
        return mBase;
    }
    
    private static SessionFactory m_sessionFactory = null;
    private SessionFactory getSessionFactory(){
    	if(m_sessionFactory != null){
    		return m_sessionFactory;
    	}
    	
    	try{
	    	m_sessionFactory = getSessionFactoryByJndi();
	    	if(m_sessionFactory != null){
	    		return m_sessionFactory;
	    	}
    	}
    	catch(IllegalStateException ilex){
    		logger.info("can not get SessionFactory by jndi, try to get it by config");
    	}
    	
    	m_sessionFactory = getSessionFactoryByConfig();
    	return m_sessionFactory;
    }
    
    /***
     * get SessionFactory by JDNI
     * @return
     */
    private SessionFactory getSessionFactoryByJndi(){
        try{
            Context ctx = new InitialContext();
            SessionFactory sessionFactory = (SessionFactory)ctx.lookup("hibernate/SessionFactory");
            return sessionFactory;
        }
        catch(Exception ex){
            logger.error("Could not locate SessionFactory in JNDI");
            logger.error(ex.getMessage(), ex);
            throw new IllegalStateException("Could not locate SessionFactory in JNDI", ex);
        }
    }
    
    /***
     * get SessionFactory by configuration file
     * @return
     */
    private SessionFactory getSessionFactoryByConfig(){
	    if(m_sessionFactory != null)
	        return m_sessionFactory;
		
	    synchronized(SessionFactory.class){
	        if(m_sessionFactory != null)
	            return m_sessionFactory;
			
	        init();
	        return m_sessionFactory;
	    }
	}
	
	private static void init(){
	    Configuration configuration = new Configuration();
	    configuration.configure();

	    m_sessionFactory = configuration.buildSessionFactory();
	}
}
